package com.moonshine.parser.finder;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import macromedia.asc.embedding.avmplus.Features;
import macromedia.asc.parser.Parser;
import macromedia.asc.parser.ProgramNode;
import macromedia.asc.util.Context;
import macromedia.asc.util.ContextStatics;

import org.sqlite.SQLiteConfig;

import com.moonshine.parser.evaluators.MoonshineSignatureEvaluator;
import com.moonshine.parser.models.AS3Signature;
import com.moonshine.parser.models.ClassBaseSignature;
import com.moonshine.parser.models.ClassSignature;
import com.moonshine.parser.models.ImportSignature;
import com.moonshine.parser.models.InterfaceSignature;
import com.moonshine.parser.models.MethodSignature;
import com.moonshine.parser.models.VariableSignature;
import com.moonshine.utils.BenchMark;
import com.moonshine.utils.FileUtils;
import com.moonshine.utils.InfoDBUtils;
import com.moonshine.utils.SignatureTools;
import com.moonshine.utils.TopLevel;

public class ClassInfoDB implements IResolver {

	private MoonshineSignatureEvaluator evaluator = new MoonshineSignatureEvaluator();
	private ContextStatics cxs = new ContextStatics();
	private Context mContext;

	private final ClassInfoCache mCache = new ClassInfoCache();

	public ClassInfoDB() {
		cxs.dialect = 10; // for as3
		cxs.setAbcVersion(Features.TARGET_AVM2);
		mContext = new Context(cxs);
	}

	private List<File> classPaths = new ArrayList<File>();

	/**
	 * Set a collection of class paths to be used for indexing/resolving
	 * 
	 * @param paths
	 */
	public void setClassPaths(String[] paths) {

		for (String classPath : paths) {

			classPaths.add(new File(classPath));

		}
	}

	/**
	 * Entry point to indexing a single file or directory
	 * 
	 * @param dirOrFile
	 *            File or directory to index
	 */
	public void index(File dirOrFile) {
		List<File> foundFiles = new ArrayList<File>();

		if (dirOrFile.isDirectory()) {
			FileUtils.findFilesRecursive(foundFiles, dirOrFile,
					new FileFilter() {

						@Override
						public boolean accept(File file) {
							// TODO Auto-generated method stub

							return file.getName().endsWith(".as");
						}

					});
		} else {
			foundFiles.add(dirOrFile);
		}

		for (File currentFileClass : foundFiles) {

			indexClassFile(currentFileClass);

		}
		// TODO Auto-generated method stub

	}

	/**
	 * Index a Single class
	 * 
	 * @param asClass
	 *            String location to file path
	 */
	public void index(String asClass) {
		indexClassFile(new File(asClass));

	}

	protected void indexClassFile(File filePath) {
		if (filePath.exists()) {
			// use Flex SDK to parse and get signature

			InputStream in = null;
			try {
				in = new BufferedInputStream(new FileInputStream(filePath));
			} catch (Exception e) {

			}
			if (in == null)
				return;

			BenchMark.start("parse " + filePath.toString());
			Parser parser = new Parser(mContext, in, "source", false, true);
			ProgramNode node = parser.parseProgram();

			BenchMark.end("parse " + filePath.toString());

			BenchMark.start("eval " + filePath.toString());

			evaluator.evaluate(mContext, node);
			BenchMark.end("eval " + filePath.toString());

			List<ClassBaseSignature> classes = evaluator.getClasses();

			for (ClassBaseSignature classSig : classes) {
				processClassSignature(classSig);
			}
		}
	}

	protected void processClassSignature(ClassBaseSignature classSig) {

	}

	protected String resolveClassVariableTypes(ClassBaseSignature classSig,
			String declaredType) throws IOException {
		List<VariableSignature> varSigs = new ArrayList<VariableSignature>();
		VariableSignature varSig = new VariableSignature();
		varSig.declaredType = declaredType;
		varSigs.add(varSig);
		resolveClassVariableTypes(classSig, varSigs);
		return varSig.declaredType;
	}

	protected void resolveClassVariableTypes(ClassBaseSignature classSig,
			List<VariableSignature> varSigs) throws IOException {

		File foundVarClassFile;
		String declaredType;
		for (VariableSignature varSig : varSigs) {
			// check to see if
			// already qualifed
			if (varSig.declaredType.equals("*")
					|| TopLevel.is(varSig.declaredType)
					|| varSig.declaredType.indexOf(":") != -1)
				continue;

			foundVarClassFile = findClassFilePath(classSig, varSig.declaredType);
			if (foundVarClassFile != null) {
				declaredType = varSig.declaredType;
				varSig.declaredType = getQualifedNameFromFilePath(foundVarClassFile
						.getAbsolutePath());
				if (varSig.declaredType == null)
					varSig.declaredType = declaredType;

			} else {
				System.out.println("Couldn't resolve varSig: "
						+ varSig.declaredType);
			}

		}

	}

	public String resolveImportedClass(String classFilePath,
			String importClassName) throws IOException {

		String classPath = getQualifedNameFromFilePath(classFilePath);
		File foundImportedClass = findClassFilePath(SignatureTools
				.getPackageName(classPath), importClassName);
		if (foundImportedClass != null) {
			return getQualifedNameFromFilePath(foundImportedClass);
		}
		return importClassName;
	}

	public String getQualifedNameFromFilePath(File filePath) throws IOException {
		return getQualifedNameFromFilePath(filePath.getCanonicalPath());
	}

	public String getQualifedNameFromFilePath(String filePath)
			throws IOException {
		String qualifedName = mCache.getQualifedNameCache(filePath);
		// remove file extenstions
		filePath = filePath.replaceFirst("\\.as|\\.mxml", "");
		String dirPath;
		for (File classPath : classPaths) {
			dirPath = classPath.getCanonicalPath();
			if (filePath.indexOf(dirPath) != -1) {
				filePath = filePath.replace(dirPath + File.separator, "");

				qualifedName = filePath.replace(File.separator, ".");

				int endIndex = qualifedName.lastIndexOf(".");
				if (endIndex != -1) {
					qualifedName = qualifedName.substring(0, endIndex) + ":"
							+ qualifedName.substring(endIndex + 1);
				}
				break;
			}

		}

		return qualifedName;

	}

	protected File findClassFilePath(ClassBaseSignature classSig,
			String className) {
		File classSameDirectory = findClassFilePath(classSig).getParentFile();

		String varClassName = SignatureTools.getClassName(className);
		final String classFileName = varClassName + ".as";

		File foundClassFile = new File(classSameDirectory, classFileName);

		if (!foundClassFile.exists()) {

			if (classSig.imports.size() != 0) {
				for (ImportSignature importSig : classSig.imports) {
					if (importSig.name.endsWith("." + varClassName)
							|| importSig.name.endsWith(varClassName)) {
						foundClassFile = findClassFilePath(SignatureTools
								.getPackageName(importSig.name), varClassName);
						break;
					}
				}
			}
			// In cases we dont't have an import statement we will to find the
			// best match from our class path, taking the first match
			// if (foundClassFile == null) {
			// List<File> foundFiles = new ArrayList<File>();
			// for (File classPath : classPaths) {
			// File[] subDirectories = classPath
			// .listFiles(new FileFilter() {
			//
			// @Override
			// public boolean accept(File file) {
			// return file.isDirectory();
			//
			// }
			//
			// });
			// for (File subDirectory : subDirectories) {
			// // ADD a caching layer for found paths
			// FileUtils.findFilesRecursive(foundFiles, subDirectory,
			// new FileFilter() {
			//
			// @Override
			// public boolean accept(File file) {
			//
			// return file.getName().equals(
			// classFileName);
			//
			// }
			//
			// });
			// if (foundFiles.size() != 0) {
			// // TODO : in cases we find duplicates
			// foundClassFile = foundFiles.get(0);
			// break;
			// }
			// }
			// }
			// }
		}

		return foundClassFile;
	}

	protected File findClassFilePath(ClassBaseSignature classSig) {
		return findClassFilePath(SignatureTools
				.getPackageName(classSig.packageName), SignatureTools
				.getClassName(classSig.name));

	}

	protected File findClassFilePath(String qualifedClassName) {
		return findClassFilePath(SignatureTools
				.getPackageName(qualifedClassName), SignatureTools
				.getClassName(qualifedClassName));
	}

	protected File findClassFilePath(String packageName, String className) {
		File foundClassFile = mCache.getFilePathCache(packageName, className);
		if (foundClassFile != null)
			return foundClassFile;
		String classFileName = className + ".as";

		String packagePath = packageName.replace(".", File.separator);
		File classFile;
		for (File classPath : classPaths) {

			classFile = new File(classPath, classFileName); // check at root
			if (classFile.exists()) {
				foundClassFile = classFile;
				break;
			} else {
				// check package path
				classFile = new File(classPath, packagePath + File.separator
						+ classFileName);
				if (classFile.exists()) {
					foundClassFile = classFile;
					break;
				}

			}

		}
		if (foundClassFile != null) {
			mCache.addFilePathCache(packageName, className, foundClassFile);
			return foundClassFile;
		}
		return null;

	}

	public void remove(String classType) {

	}

	@Override
	public String resolveType(String clazz, String property) throws IOException {
		// TODO Auto-generated method stub
		// TODO: Query DB or cache layer
		return "Object";
	}

	public List<AS3Signature> resolveCompletionList(String forClass, int flags) {
		return new ArrayList<AS3Signature>();
	}

}
